package com.sgcc.nfc.lock.presentation.rooms

import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import androidx.appcompat.app.AlertDialog
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Bundle
import android.os.Looper
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.google.android.material.snackbar.Snackbar
import com.sgcc.nfc.lock.BuildConfig
import com.sgcc.nfc.lock.R
import com.sgcc.nfc.lock.core.location.CoordinateConverter
import com.sgcc.nfc.lock.databinding.FragmentRoomFormBinding
import com.sgcc.nfc.lock.domain.model.Region
import com.sgcc.nfc.lock.domain.model.RegionLevel
import com.sgcc.nfc.lock.domain.repository.RoomRepository
import com.sgcc.nfc.lock.presentation.rooms.LocationPickerActivity.Companion.EXTRA_LATITUDE
import com.sgcc.nfc.lock.presentation.rooms.LocationPickerActivity.Companion.EXTRA_LONGITUDE
import dagger.hilt.android.AndroidEntryPoint
import java.util.Locale
import java.util.concurrent.TimeUnit
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONObject

@AndroidEntryPoint
class RoomFormFragment : Fragment() {

    private var _binding: FragmentRoomFormBinding? = null
    private val binding get() = _binding!!
    private val viewModel: RoomFormViewModel by viewModels()

    private var regionOptions: List<RegionOption> = emptyList()
    private var selectedRegionId: Long? = null
    private var latitude: Double? = null
    private var longitude: Double? = null
    private var hasInitializedRoom = false
    private var autoFillLocationManager: LocationManager? = null
    private var autoFillLocationListener: LocationListener? = null
    private var autoFilledAddress: String? = null
    private var addressManuallyEdited = false
    private var suppressAddressWatcher = false
    private var geocodeJob: Job? = null
    private val geocodeClient by lazy {
        OkHttpClient.Builder()
            .connectTimeout(5, TimeUnit.SECONDS)
            .readTimeout(5, TimeUnit.SECONDS)
            .writeTimeout(5, TimeUnit.SECONDS)
            .build()
    }

    private val pickLocationLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == Activity.RESULT_OK) {
            val data = result.data
            val lat = data?.getDoubleExtra(EXTRA_LATITUDE, Double.NaN)
            val lng = data?.getDoubleExtra(EXTRA_LONGITUDE, Double.NaN)
            if (lat != null && !lat.isNaN() && lng != null && !lng.isNaN()) {
                updateLocationFields(lat, lng)
            }
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentRoomFormBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setupListeners()
        setupAddressInputWatcher()
        observeState()
    }

    private fun setupListeners() {
        binding.selectLocationButton.setOnClickListener {
            val intent = LocationPickerActivity.createIntent(requireContext(), latitude, longitude)
            pickLocationLauncher.launch(intent)
        }
        binding.cancelButton.setOnClickListener { requireActivity().onBackPressedDispatcher.onBackPressed() }
        binding.saveButton.setOnClickListener { submitForm() }
        
        // 设置区域选择点击事件
        binding.roomRegionEdit.setOnClickListener {
            showRegionSelectionDialog()
        }
    }

    private fun setupAddressInputWatcher() {
        binding.roomAddressEdit.addTextChangedListener { text ->
            if (suppressAddressWatcher) return@addTextChangedListener
            val current = text?.toString().orEmpty()
            addressManuallyEdited = current.isNotBlank() && current != autoFilledAddress
        }
    }

    private fun observeState() {
        viewModel.uiState.observe(viewLifecycleOwner) { state ->
            binding.roomFormLoading.isVisible = state.isLoading || state.isSubmitting
            binding.saveButton.isEnabled = !state.isSubmitting
            binding.cancelButton.isEnabled = !state.isSubmitting
            binding.selectLocationButton.isEnabled = !state.isSubmitting
            if (state.error != null) {
                Snackbar.make(binding.root, state.error, Snackbar.LENGTH_LONG).show()
            }
            if (!state.isLoading) {
                setupRegionDropdown(state.regions)
                if (!hasInitializedRoom && state.room != null) {
                    populateForm(state.room)
                    hasInitializedRoom = true
                } else if (!hasInitializedRoom && !viewModel.isEdit) {
                    hasInitializedRoom = true
                    tryPopulateCurrentLocation()
                }
            }
        }
        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.events.collect { event ->
                    when (event) {
                        is RoomFormEvent.ShowMessage -> Snackbar.make(binding.root, event.message, Snackbar.LENGTH_LONG).show()
                        is RoomFormEvent.SubmitSuccess -> {
                            val messageRes = when (event.type) {
                                RoomFormEvent.OperationType.CREATE -> R.string.room_create_success
                                RoomFormEvent.OperationType.UPDATE -> R.string.room_update_success
                            }
                            Snackbar.make(binding.root, getString(messageRes), Snackbar.LENGTH_SHORT).show()
                            notifyFormCompleted()
                            requireActivity().onBackPressedDispatcher.onBackPressed()
                        }
                    }
                }
            }
        }
    }

    private fun setupRegionDropdown(regions: List<Region>) {
        if (regions.isEmpty()) {
            binding.roomRegionEdit.text?.clear()
            binding.roomRegionLayout.isEnabled = false
            binding.roomRegionEdit.isEnabled = false
            binding.roomRegionLayout.error = getString(R.string.room_form_region_empty)
            binding.saveButton.isEnabled = false
            return
        }
        regionOptions = buildRegionOptions(regions)
        if (regionOptions.isEmpty()) {
            binding.roomRegionEdit.text?.clear()
            binding.roomRegionLayout.isEnabled = false
            binding.roomRegionEdit.isEnabled = false
            binding.roomRegionLayout.error = getString(R.string.room_form_region_empty)
            binding.saveButton.isEnabled = false
            return
        }
        val canSubmit = viewModel.uiState.value?.isSubmitting?.not() ?: true
        binding.saveButton.isEnabled = canSubmit
        binding.roomRegionLayout.isEnabled = true
        binding.roomRegionEdit.isEnabled = true
        binding.roomRegionLayout.error = null
        
        // 如果只有一个区域选项且不是编辑模式，自动选择
        if (regionOptions.size == 1 && selectedRegionId == null && !viewModel.isEdit) {
            regionOptions.firstOrNull()?.let { option ->
                selectedRegionId = option.id
                binding.roomRegionEdit.setText(option.label)
            }
        }
        
        // 如果有选中的区域ID，显示对应的标签
        if (selectedRegionId != null) {
            val selected = regionOptions.firstOrNull { it.id == selectedRegionId }
            if (selected != null) {
                binding.roomRegionEdit.setText(selected.label)
            } else {
                selectedRegionId = null
                binding.roomRegionEdit.text?.clear()
            }
        } else if (viewModel.isEdit && viewModel.uiState.value?.room?.regionId != null) {
            val selected = regionOptions.firstOrNull { it.id == viewModel.uiState.value?.room?.regionId }
            selected?.let {
                binding.roomRegionEdit.setText(it.label)
                selectedRegionId = it.id
            }
        }
    }

    private fun populateForm(room: com.sgcc.nfc.lock.domain.model.Room) {
        binding.formTitle.setText(R.string.room_form_title_edit)
        binding.roomNameEdit.setText(room.roomName)
        suppressAddressWatcher = true
        binding.roomAddressEdit.setText(room.address)
        binding.roomAddressEdit.setSelection(room.address?.length ?: 0)
        suppressAddressWatcher = false
        autoFilledAddress = room.address
        addressManuallyEdited = false

        latitude = room.latitude
        longitude = room.longitude
        val lat = room.latitude
        val lng = room.longitude
        if (lat != null) {
            binding.roomLatitudeEdit.setText(String.format(Locale.CHINA, "%.6f", lat))
            binding.roomLatitudeLayout.error = null
        }
        if (lng != null) {
            binding.roomLongitudeEdit.setText(String.format(Locale.CHINA, "%.6f", lng))
            binding.roomLongitudeLayout.error = null
        }
        if (room.address.isNullOrBlank() && lat != null && lng != null) {
            requestReverseGeocode(lat, lng)
        }
        selectedRegionId = room.regionId
    }

    private fun submitForm() {
        val name = binding.roomNameEdit.text?.toString()?.trim().orEmpty()
        val regionId = selectedRegionId
        val lat = latitude
        val lng = longitude
        var hasError = false
        if (name.isBlank()) {
            binding.roomNameLayout.error = getString(R.string.room_validation_name)
            hasError = true
        } else {
            binding.roomNameLayout.error = null
        }
        if (regionId == null) {
            binding.roomRegionLayout.error = getString(R.string.room_validation_region)
            hasError = true
        } else {
            binding.roomRegionLayout.error = null
        }
        if (lat == null || lng == null) {
            binding.roomLatitudeLayout.error = getString(R.string.room_validation_location)
            binding.roomLongitudeLayout.error = getString(R.string.room_validation_location)
            hasError = true
        } else {
            binding.roomLatitudeLayout.error = null
            binding.roomLongitudeLayout.error = null
        }
        if (hasError) return

        if (viewModel.isEdit) {
            val roomId = viewModel.uiState.value?.room?.roomId ?: return
            val params = RoomRepository.UpdateRoomParams(
                roomId = roomId,
                roomName = name,
                regionId = regionId!!,
                longitude = lng!!,
                latitude = lat!!,
                address = binding.roomAddressEdit.text?.toString()?.trim()
            )
            viewModel.updateRoom(params)
        } else {
            val params = RoomRepository.CreateRoomParams(
                roomName = name,
                regionId = regionId!!,
                longitude = lng!!,
                latitude = lat!!,
                address = binding.roomAddressEdit.text?.toString()?.trim()
            )
            viewModel.createRoom(params)
        }
    }

    private fun notifyFormCompleted() {
        findNavController().previousBackStackEntry?.savedStateHandle?.set(RESULT_KEY_ROOM_UPDATED, true)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        cleanupAutoFillLocation()
        geocodeJob?.cancel()
        geocodeJob = null
        _binding = null
    }

    private fun tryPopulateCurrentLocation() {
        if (latitude != null && longitude != null) return
        val context = context ?: return
        if (!hasLocationPermission(context)) return
        val manager = (autoFillLocationManager ?: context.getSystemService(Context.LOCATION_SERVICE) as? LocationManager)
            ?: return
        autoFillLocationManager = manager

        val providers = listOf(LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER)
        providers.forEach { provider ->
            if (manager.isProviderEnabled(provider)) {
                @Suppress("MissingPermission")
                val lastLocation = runCatching { manager.getLastKnownLocation(provider) }.getOrNull()
                if (lastLocation != null) {
                    updateLocationFromWgs(lastLocation.latitude, lastLocation.longitude)
                    return
                }
            }
        }

        if (autoFillLocationListener != null) return
        val provider = providers.firstOrNull { manager.isProviderEnabled(it) } ?: return
        val listener = object : LocationListener {
            override fun onLocationChanged(location: Location) {
                updateLocationFromWgs(location.latitude, location.longitude)
                cleanupAutoFillLocation()
            }

            override fun onProviderDisabled(provider: String) = Unit
            override fun onProviderEnabled(provider: String) = Unit
            @Deprecated("Deprecated in Java")
            override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) = Unit
        }
        autoFillLocationListener = listener
        @Suppress("MissingPermission")
        manager.requestSingleUpdate(provider, listener, Looper.getMainLooper())
    }

    private fun updateLocationFields(lat: Double, lng: Double) {
        latitude = lat
        longitude = lng
        _binding?.let { binding ->
            binding.roomLatitudeEdit.setText(String.format(Locale.CHINA, "%.6f", lat))
            binding.roomLongitudeEdit.setText(String.format(Locale.CHINA, "%.6f", lng))
            binding.roomLatitudeEdit.setSelection(binding.roomLatitudeEdit.text?.length ?: 0)
            binding.roomLongitudeEdit.setSelection(binding.roomLongitudeEdit.text?.length ?: 0)
            binding.roomLatitudeLayout.error = null
            binding.roomLongitudeLayout.error = null
        }
        requestReverseGeocode(lat, lng)
    }

    private fun updateLocationFromWgs(lat: Double, lng: Double) {
        val (convertedLat, convertedLng) = CoordinateConverter.wgs84ToGcj02(lat, lng)
        updateLocationFields(convertedLat, convertedLng)
    }

    private fun requestReverseGeocode(lat: Double, lng: Double) {
        if (addressManuallyEdited) return
        val apiKey = BuildConfig.AMAP_API_KEY.takeIf { it.isNotBlank() } ?: return
        geocodeJob?.cancel()
        geocodeJob = viewLifecycleOwner.lifecycleScope.launch {
            val locationParam = String.format(Locale.US, "%.6f,%.6f", lng, lat)
            val url = HttpUrl.Builder()
                .scheme("https")
                .host("restapi.amap.com")
                .addPathSegments("v3/geocode/regeo")
                .addQueryParameter("key", apiKey)
                .addQueryParameter("location", locationParam)
                .addQueryParameter("extensions", "base")
                .addQueryParameter("radius", "200")
                .build()
            val request = Request.Builder()
                .url(url)
                .get()
                .build()
            val address = withContext(Dispatchers.IO) {
                runCatching {
                    geocodeClient.newCall(request).execute().use { response ->
                        if (!response.isSuccessful) return@use null
                        val body = response.body?.string() ?: return@use null
                        parseAddress(body)
                    }
                }.getOrNull()
            }
            if (!address.isNullOrBlank()) {
                applyAutoAddress(address)
            }
        }
    }

    private fun parseAddress(payload: String): String? {
        return runCatching {
            val root = JSONObject(payload)
            if (root.optInt("status") != 1) return@runCatching null
            val regeocode = root.optJSONObject("regeocode") ?: return@runCatching null
            val formatted = regeocode.optString("formatted_address").takeIf { it.isNotBlank() }
            val fallback = regeocode.optJSONObject("addressComponent")
                ?.optString("district")
                ?.takeIf { it.isNotBlank() }
            formatted ?: fallback
        }.getOrNull()
    }

    private fun applyAutoAddress(address: String) {
        if (addressManuallyEdited) return
        suppressAddressWatcher = true
        autoFilledAddress = address.trim().takeIf { it.isNotEmpty() }
        _binding?.roomAddressEdit?.let { input ->
            val value = autoFilledAddress.orEmpty()
            input.setText(value)
            input.setSelection(value.length)
        }
        suppressAddressWatcher = false
        addressManuallyEdited = false
    }

    private fun cleanupAutoFillLocation() {
        autoFillLocationListener?.let { listener ->
            autoFillLocationManager?.removeUpdates(listener)
        }
        autoFillLocationListener = null
        autoFillLocationManager = null
    }

    private fun hasLocationPermission(context: Context): Boolean =
        ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED

    private fun showRegionSelectionDialog() {
        if (regionOptions.isEmpty()) {
            Snackbar.make(binding.root, getString(R.string.room_form_region_empty), Snackbar.LENGTH_SHORT).show()
            return
        }
        
        val labels = regionOptions.map { it.label }.toTypedArray()
        val currentIndex = regionOptions.indexOfFirst { it.id == selectedRegionId }
        
        AlertDialog.Builder(requireContext())
            .setTitle(R.string.room_form_region_select_title)
            .setSingleChoiceItems(labels, currentIndex) { dialog, which ->
                val selectedOption = regionOptions[which]
                selectedRegionId = selectedOption.id
                binding.roomRegionEdit.setText(selectedOption.label)
                binding.roomRegionLayout.error = null
                dialog.dismiss()
            }
            .setNegativeButton(android.R.string.cancel, null)
            .show()
    }

    private fun buildRegionOptions(regions: List<Region>): List<RegionOption> {
        val options = mutableListOf<RegionOption>()
        fun append(region: Region, depth: Int) {
            val level = RegionLevel.fromValue(region.regionType ?: region.level)
            if (level == RegionLevel.STATION) {
                region.regionId?.let { id ->
                    val label = buildString {
                        repeat(depth) { append("   ") }
                        append(region.regionName)
                    }
                    options += RegionOption(id, label)
                }
            }
            region.children.forEach { child -> append(child, depth + 1) }
        }
        regions.forEach { append(it, 0) }
        return options
    }

    private data class RegionOption(val id: Long, val label: String)

    companion object {
        const val RESULT_KEY_ROOM_UPDATED = "room_updated"
    }
}
